﻿using System;
using System.Collections.Generic;
using System.Linq;

using Mvc.XForms.Examples.Model;
using Mvc.XForms.Utilities;

namespace Mvc.XForms.Examples.Repository {
	/// <summary>
	/// An in-memory database of IEntity objects. Need to find some way of
	/// specifying cascades for update/delete (maybe should just use SQLite).
	/// </summary>
	public class InMemoryDataContext : List<IEntity> {
		public InMemoryDataContext() {
			AddPlanets();
			AddMailingLists();
			AddProducts();
			AddAddressess();
			AddCustomers();
		}

		public IQueryable<TEntity> AsQueryable<TEntity>() where TEntity : IEntity {
			var entities = this
				.Where(obj => obj is TEntity)
				.Cast<TEntity>()
				.AsQueryable();

			return entities;
		}

		public TEntity Get<TEntity>(long entityID) where TEntity : IEntity {
			return Get<TEntity>(e => e.ID == entityID);
		}

		private TEntity Get<TEntity>(Func<TEntity, bool> predicate) where TEntity : IEntity {
			var entity = AsQueryable<TEntity>().Single(predicate);
			return entity;
		}

		private IList<TEntity> GetList<TEntity>() where TEntity : IEntity {
			return AsQueryable<TEntity>().ToList();
		}

		private IList<TEntity> GetList<TEntity>(Func<TEntity, bool> predicate) where TEntity : IEntity {
			return AsQueryable<TEntity>().Where(predicate).ToList();
		}

		public void AddEntity<TEntity>(TEntity entity) where TEntity : IEntity {
			// Get the next ID (we are mocking identity insert here)
			var list = AsQueryable<TEntity>();
			if (list.Count() > 0)
				entity.ID = list.Max(c => c.ID) + 1;
			else
				entity.ID = 1;
			Add(entity);
		}

		private void AddPlanets() {
			var planets = new string[] { "Alderaan", "Coruscant", "Dagobah", "Endor", "Hoth", "Tatooine", "Yavin 4" };

			planets.ForEach(x => {
				AddEntity(new Planet { Name = x });
			});
		}

		private void AddMailingLists() {
			var mailingLists = new string[] { "Dark-side News", "Rebel Alliance Monthly", "Wookie Quarterly", "Timeout Tatooine" };

			mailingLists.ForEach(x => {
				AddEntity(new MailingList { Name = x });
			});
		}

		private void AddProducts() {
			var rand = new Random();
			var productNames = new string[] { "Blaster", "X-Wing", "Lightsaber", "Death star", "At-at", "Snow speeder", "Ewok", "Protocol droid", "Land speeder", "TIE fighter" };

			productNames.ForEach((x, i) => {
				AddEntity(new Product { Name = x, Price = rand.Next(1, 9) * 100 + 99 });
			});
		}

		private void AddAddressess() {
			Enumerable.Range(0, 10).ForEach(i => {
				AddEntity(new Address { Address1 = "Address 1", Address2 = "Address 2" });
			});
		}

		private void AddCustomers() {
			var random = new Random();
			var customerNames = new string[] { "R2D2", "C-3PO", "Luke Skywalker", "Princess Leia", "Han Solo", "Lando Calrissian", "Darth Vadar", "Bib Fortuna", "Obi-Wan Kenobi", "Jabba The Hutt" };
			var factions = new Faction[] { Faction.RebelAlliance, Faction.RebelAlliance, Faction.RebelAlliance, Faction.RebelAlliance, Faction.Republic, Faction.Republic, Faction.Empire, Faction.Empire, Faction.RebelAlliance, Faction.Republic };

			var addressess = GetList<Address>();
			var customers = customerNames.Select((name, i) =>
				new Customer {
					Guid = Guid.NewGuid(),
					IsActive = true,
					Name = name,
					Faction = factions[i],
					DateOfBirth = DateTime.Now.AddYears(-5 - (i * 10)),
					Notes = new List<string> { "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque facilisis, lacus vitae malesuada eleifend, ligula ligula eleifend felis, sit amet rhoncus nunc justo sed nulla." },
					Address = addressess[i],
					Orders = new List<Order> { new Order { Reference = Guid.NewGuid().ToString().Substring(0, 8) } }
				}
			).ToList();

			customers[0].Address.Planet = Get<Planet>(4);
			customers[1].Address.Planet = Get<Planet>(6);
			customers[2].Address.Planet = Get<Planet>(6);
			customers[3].Address.Planet = Get<Planet>(1);
			customers[4].Address.Planet = Get<Planet>(2);
			customers[5].Address.Planet = Get<Planet>(7);
			customers[6].Address.Planet = Get<Planet>(5);
			customers[7].Address.Planet = Get<Planet>(7);
			customers[8].Address.Planet = Get<Planet>(2);
			customers[9].Address.Planet = Get<Planet>(3);

			var productList = GetList<Product>();
			customers[0].Orders[0].Products.Add(productList[0]);
			customers[2].Orders[0].Products.Add(productList[1]);
			customers[2].Orders[0].Products.Add(productList[2]);
			customers[3].Orders[0].Products.Add(productList[6]);
			customers[3].Orders[0].Products.Add(productList[4]);
			customers[3].Orders[0].Products.Add(productList[3]);
			customers[4].Orders[0].Products.Add(productList[5]);
			customers[4].Orders[0].Products.Add(productList[8]);
			customers[4].Orders[0].Products.Add(productList[1]);
			customers[5].Orders[0].Products.Add(productList[4]);
			customers[6].Orders[0].Products.Add(productList[0]);
			customers[6].Orders[0].Products.Add(productList[1]);
			customers[7].Orders[0].Products.Add(productList[9]);
			customers[8].Orders[0].Products.Add(productList[5]);
			customers[8].Orders[0].Products.Add(productList[7]);
			customers[8].Orders[0].Products.Add(productList[2]);
			customers[9].Orders[0].Products.Add(productList[9]);

			var mailingList = GetList<MailingList>();
			customers[0].MailingLists.Add(mailingList[0]);
			customers[2].MailingLists.Add(mailingList[1]);
			customers[2].MailingLists.Add(mailingList[2]);
			customers[3].MailingLists.Add(mailingList[1]);
			customers[3].MailingLists.Add(mailingList[2]);
			customers[3].MailingLists.Add(mailingList[3]);
			customers[4].MailingLists.Add(mailingList[0]);
			customers[4].MailingLists.Add(mailingList[1]);
			customers[4].MailingLists.Add(mailingList[3]);
			customers[5].MailingLists.Add(mailingList[1]);
			customers[6].MailingLists.Add(mailingList[2]);
			customers[6].MailingLists.Add(mailingList[3]);
			customers[7].MailingLists.Add(mailingList[0]);
			customers[8].MailingLists.Add(mailingList[0]);
			customers[8].MailingLists.Add(mailingList[2]);
			customers[8].MailingLists.Add(mailingList[3]);
			customers[9].MailingLists.Add(mailingList[0]);

			customers.ForEach(c => AddEntity(c));
		}
	}
}